package com.mlx.permission.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: sss
 * @DateTime: 2021-09-18 15:05
 * @author: mlx
 * @version: 1.0
 **/
public class BeanUtils {

    /**
     * Class类型的getMethod方法获取不到指定方法时指定抛出异常，重写此方法可以给机会判断获取到和获取不到后再抛出异常
     *
     * @param objClass 方法所在类
     * @param name 方法名称
     * @param parameterTypes 参数类型列表
     * @return 方法对象
     */
    public static Method getMethod(Class<?> objClass, String name, Class<?>... parameterTypes){

        if (name == null || name.isEmpty()){
            return null;
        }
        if (objClass == null){
            return null;
        }

        Method[] methods = objClass.getMethods();
        for (Method method : methods){
            if (method.getName().equals(name) && method.getParameterCount() == parameterTypes.length){
                Class<?>[] methodParameterTypes = method.getParameterTypes();
                boolean all = true;
                for (int i = 0 ; i < parameterTypes.length ; i++){
                    if (methodParameterTypes[i] != parameterTypes[i]){
                        all = false;
                        break;
                    }
                }
                if (all){
                    return method;
                }
            }
        }

        return null;
    }

    /**
     * 更精确的getMethod方法
     * @param objClass 方法所在类
     * @param modifier 方法修饰符
     * @param returnType 方法返回类型
     * @param name 方法名称
     * @param parameterTypes 参数类型列表
     * @return 方法对象
     */
    public static Method getMethod(Class<?> objClass, int modifier, Class<?> returnType, String name, Class<?>... parameterTypes){

        if (name == null || name.isEmpty()){
            return null;
        }
        if (objClass == null){
            return null;
        }
        if (returnType == null){
            return null;
        }

        Method[] methods = objClass.getMethods();
        for (Method method : methods){
            if (method.getName().equals(name) && method.getModifiers() == modifier && method.getParameterCount() == parameterTypes.length && method.getReturnType() == returnType){
                Class<?>[] methodParameterTypes = method.getParameterTypes();
                boolean all = true;
                for (int i = 0 ; i < parameterTypes.length ; i++){
                    if (methodParameterTypes[i] != parameterTypes[i]){
                        all = false;
                        break;
                    }
                }
                if (all){
                    return method;
                }
            }
        }

        return null;
    }

    private static void throwException(String format, Object... objects){
        throw new RuntimeException(String.format(format, objects));
    }


    /**
     * 模拟内省机制，获取get方法map，key值为属性名称小写
     * @param beanClass 对象的类型
     * @return get方法
     */
    public static Map<String, Method> getGetterMethodMap(Class<?> beanClass){
        Map<String, Method> methodMap = new HashMap<>();
        BeanInfo beanInfo = null;
        try {
            beanInfo = Introspector.getBeanInfo(beanClass);
        } catch (IntrospectionException e) {
            throwException("获取【%s】getter失败，原因是获取【%s】BeanInfo发生异常，异常信息：%s", beanClass, beanClass, e.getMessage());
        }
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        if (propertyDescriptors != null){
            for (PropertyDescriptor descriptor : propertyDescriptors){
                if (descriptor.getReadMethod() != null){
                    methodMap.put(descriptor.getName(), descriptor.getReadMethod());
                }
            }
        }
        return methodMap;
    }

    /**
     * 模拟内省机制，获取get方法map，key值为属性名称小写
     *
     * @param beanClass 对象的类型
     * @return set方法
     */
    public static Map<String, Method> getSetterMethodMap(Class<?> beanClass){
        Map<String, Method> methodMap = new HashMap<>();
        BeanInfo beanInfo = null;
        try {
            beanInfo = Introspector.getBeanInfo(beanClass);
        } catch (IntrospectionException e) {
            throwException("获取【%s】setter失败，原因是获取【%s】BeanInfo发生异常，异常信息：%s", beanClass, beanClass, e.getMessage());
        }
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        if (propertyDescriptors != null){
            for (PropertyDescriptor descriptor : propertyDescriptors){
                if (descriptor.getWriteMethod() != null){
                    methodMap.put(descriptor.getName(), descriptor.getWriteMethod());
                }

            }
        }
        return methodMap;
    }

    public static Map<String, PropertyDescriptor> getBeanPropertyDescriptorMap(Class<?> beanClass){
        Map<String, PropertyDescriptor> descriptorMap = new HashMap<>();
        BeanInfo beanInfo = null;
        try {
            beanInfo = Introspector.getBeanInfo(beanClass);
        } catch (IntrospectionException e) {
            throwException("获取【%s】setter失败，原因是获取【%s】BeanInfo发生异常，异常信息：%s", beanClass, beanClass, e.getMessage());
        }

        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        if (propertyDescriptors != null){
            for (PropertyDescriptor descriptor : propertyDescriptors){
                if (descriptor.getWriteMethod() != null && descriptor.getReadMethod() != null){
                    descriptorMap.put(descriptor.getName(), descriptor);
                }
            }
        }

        return descriptorMap;
    }

    /**
     * 将源对象的属性赋值给目标对象
     *
     * @param source 源对象
     * @param target 目标对象
     */
    public static void copyProperties(Object source, Object target){
        if (source == null){
            throw new RuntimeException("源对象为null，无法复制属性");
        }
        if (target == null){
            throw new RuntimeException("目标对象为null，无法复制属性");
        }

        // 获取源对象getter方法
        Map<String, Method> sourceGetterMethodMap = getGetterMethodMap(source.getClass());
        // 获取目标对象setter方法
        Map<String, Method> targetSetterMethodMap = getSetterMethodMap(target.getClass());

        // 判断各getter或setter方法是否空
        if (sourceGetterMethodMap.isEmpty()){
            return;
        }
        if (targetSetterMethodMap.isEmpty()){
            return;
        }

        // 获取源对象所有属性
        Set<String> targetFieldSet = targetSetterMethodMap.keySet();

        for (String field : targetFieldSet){

            // 获取源对象field属性getter方法
            Method sourceGetter = sourceGetterMethodMap.get(field);
            // 获取目标对象field属性setter方法
            Method targetSetter = targetSetterMethodMap.get(field);

            if ((sourceGetter != null) && (sourceGetter.getReturnType() == targetSetter.getParameterTypes()[0])){

                Object sourceFieldValue = null;

                try {
                    sourceFieldValue = sourceGetter.invoke(source);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(String.format("属性复制失败：没有source源对象【%s】属性getter方法的访问权限", field));
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(String.format("属性复制失败，执行source源对象【%s】属性getter方法失败，失败原因：%s", field, e.getMessage()));
                }

                try {
                    targetSetter.invoke(target, sourceFieldValue);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(String.format("属性复制失败：没有target目标对象【%s】属性setter方法的访问权限", field));
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(String.format("属性复制失败，执行target目标对象【%s】属性setter方法失败，失败原因：%s", field, e.getMessage()));
                }
            }
        }
    }


}
